#include "FloatArithmetic.h"

#define MAX_ORDER_DIFF 9
#define EXP_ACCURACY 7				//8 in fact
// Factorial n { 2, 6, 24, 120, 720, 5040, 40320 };
static const int InvFact_Mantissa[] = { 5, 166666667, 416666667,
											833333333, 138888889,
											198412698, 248015873 
										};
static const sbyte InvFact_Order[] = { -1, -9, -10, -11, -11, -12, -13 };

/*
0,5
0,16666666666666666666666666666667
0,04166666666666666666666666666667
0,00833333333333333333333333333333
0.00138888888888
1,984126984126984126984126984127e-4				
2,4801587301587301587301587301587e-5			
*/
	
byte ILager(int x, sbyte x_o, int y, sbyte y_o)
{
	register long long int _mantissa = 0;

	if(x_o == y_o)
		return x > y;

	else if(x_o < y_o)
	{
		_mantissa = y;
		
		while(--y_o != x_o)
			_mantissa *= 10;
	
		_mantissa *= 10;

		return x > _mantissa;
	}

	else
	{
		_mantissa = x;

		while(--x_o != y_o)
			_mantissa *= 10;

		_mantissa *= 10;
	
		return x > _mantissa;
	}
}

byte ILess(int x, sbyte x_o, int y, sbyte y_o)
{
	return 0;
}

void IMult(int x, sbyte x_o, int y, sbyte y_o, int* res, sbytep res_o)
{
	register long long int _mantissa = 0;

	*res_o = 0;
	
	if(x == 0 || y == 0)
	{
		*res = 0;
		return;
	}

	_mantissa = x;
	_mantissa *= y;

	*res_o = x_o + y_o;

	if(_mantissa > 0)
	{
		while(_mantissa & 0x7fffffff80000000)
		{
			_mantissa = _mantissa / 10;
			*res_o += 1;
		}
	}

	else
	{
		_mantissa = -_mantissa;

		while(_mantissa & 0x7fffffff80000000)
		{
			_mantissa = _mantissa / 10;
			*res_o += 1;
		}

		_mantissa = -_mantissa;
	}

	*res = (int)_mantissa;
}

void IDiv(int x, sbyte x_o, int y, sbyte y_o, int* res, sbytep res_o)
{
	register long long int _mantissa = 0;

	*res_o = 0;
	
	if(x == 0 || y == 0)
	{
		*res = 0;
		return;
	}

	_mantissa = x;
	_mantissa *= 1000000000;
	_mantissa /= y;

	*res_o = x_o - y_o - 9;
	
	if(_mantissa > 0)
	{
		while(_mantissa & 0x7fffffff80000000)
		{
			_mantissa = _mantissa / 10;
			*res_o += 1;
		}
	}

	else
	{
		_mantissa = -_mantissa;

		while(_mantissa & 0x7fffffff80000000)
		{
			_mantissa = _mantissa / 10;
			*res_o += 1;
		}

		_mantissa = -_mantissa;
	}
	
	*res = (int)_mantissa;
}

void ISub(int x, sbyte x_o, int y, sbyte y_o, int* res, sbytep res_o)
{
	register long long int _mantissa = 0;
	register byte neg = false;

	if(x == 0)
	{
		*res = -y;
		*res_o = y_o;
		return;
	}

	if(y == 0)
	{
		*res = x;
		*res_o = x_o;
		return;
	}

	*res_o = 0;

	if(x_o == y_o)
	{
		_mantissa = x; 
		_mantissa -= y;
	}

	else if(x_o < y_o)
	{
		*res_o = y_o;
		neg = (y < 0) ? true : false;
		_mantissa = (neg) ? -y : y;

		while(x_o != y_o)
		{
			if((_mantissa & 0x7fffffffffffffff) > 0x0cccccccc0000000)
			{
				*res = -y;
				return;
			}

			_mantissa *= 10;
			y_o--;
		}

		if(!neg)
			_mantissa = -_mantissa;

		_mantissa += x;
	}

	else
	{
		*res_o = x_o;
		neg = (x < 0) ? true : false;
		_mantissa = (neg) ? -x : x;

		while(x_o != y_o)
		{
			if((_mantissa & 0x7fffffffffffffff) > 0x0cccccccc0000000)
			{
				*res = x;
				return;
			}

			_mantissa *= 10;
			x_o--;
		}
		
		if(neg)
			_mantissa = -_mantissa;
		
		_mantissa -= y;
	}
	
	*res_o = x_o;

	if(_mantissa > 0)
	{
		while(_mantissa & 0x7fffffff80000000)
		{
			_mantissa = _mantissa/10;
			*res_o += 1;
		}
	}

	else
	{
		_mantissa = -_mantissa;

		while(_mantissa & 0x7fffffff80000000)
		{
			_mantissa = _mantissa / 10;
			*res_o += 1;
		}

		_mantissa = -_mantissa;
	}

	*res = (int)_mantissa;
}

void ISum(int x, sbyte x_o, int y, sbyte y_o, int* res, sbytep res_o)
{
	register long long int _mantissa = 0;
	register byte neg = false;

	if(x == 0)
	{
		*res = y;
		*res_o = y_o;
		
		return;
	}

	if(y == 0)
	{
		*res = x;
		*res_o = x_o;
		
		return;
	}

	*res = 0;

	if(x_o == y_o)
	{
		_mantissa = x; 
		_mantissa += y;
	}

	else if(x_o > y_o)
	{
		*res_o = x_o;
		neg = (x < 0) ? true : false;
		_mantissa = (neg) ? -x : x;

		while(x_o != y_o)
		{
			if((_mantissa & 0x7fffffffffffffff) > 0x0cccccccc0000000)
			{
				*res = x;
				return;
			}

			_mantissa *= 10;
			x_o--;
		}

		if(neg)
			_mantissa = -_mantissa;

		_mantissa += y;
	}
		
	else
	{
		*res_o = y_o;

		neg = (y < 0) ? true : false;
		_mantissa = (neg) ? -y : y;
		
		while(--y_o != x_o)
		{
			if((_mantissa & 0x7fffffffffffffff) > 0x0cccccccc0000000)
			{
				*res = y;
				return;
			}
			_mantissa *= 10;
		}

		if(neg)
			_mantissa = -_mantissa;

		_mantissa *= 10;
		_mantissa += x;
	}
	
	*res_o = x_o;

	if(_mantissa > 0)
	{
		while(_mantissa & 0x7fffffff80000000)
		{
			_mantissa = _mantissa / 10;
			*res_o += 1;
		}
	}

	else
	{
		_mantissa = -_mantissa;

		while(_mantissa & 0x7fffffff80000000)
		{
			_mantissa = _mantissa / 10;
			*res_o += 1;
		}

		_mantissa = -_mantissa;
	}
	
	*res = (int)_mantissa;
}

void IExp(int* x, sbytep x_o)
{
	register int i = 0;

	register int _x = 0;
	register sbyte _x_o = 0;

	if(*x == 0)
	{
		*x = 1;
		*x_o = 0;
		
		return;
	}

	_x = *x;
	_x_o = *x_o;

	IMult(*x, *x_o, InvFact_Mantissa[EXP_ACCURACY - 1], InvFact_Order[EXP_ACCURACY - 1], x, x_o);
	ISum(*x, *x_o, InvFact_Mantissa[EXP_ACCURACY - 2], InvFact_Order[EXP_ACCURACY - 2], x, x_o);

	for(i = EXP_ACCURACY - 3; i >= 0; i--)
	{
		IMult(*x, *x_o, _x, _x_o, x, x_o);
		ISum(*x, *x_o, InvFact_Mantissa[i], InvFact_Order[i], x, x_o);
	}
	
	IMult(*x, *x_o, _x, _x_o, x, x_o);
	ISum(*x, *x_o, 1, 0, x, x_o);
	IMult(*x, *x_o, _x, _x_o, x, x_o);

}

void ISqr(int* x, sbytep x_o)
{
	register long long int _mantissa = 0;

	_mantissa = *x;
	_mantissa *= *x;

	*x_o *= 2;

	if(_mantissa > 0)
	{
		while(_mantissa & 0x7fffffff80000000)
		{
			_mantissa = _mantissa / 10;
			*x_o += 1;
		}
	}

	else
	{
		_mantissa = -_mantissa;

		while(_mantissa & 0x7fffffff80000000)
		{
			_mantissa = _mantissa / 10;
			*x_o += 1;
		}

		_mantissa = -_mantissa;
	}

	*x = (int)_mantissa;
}